﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Clandestine
{
    public struct Vector
    {
        public int X;
        public int Y;

        public Vector(int v1, int v2)
        {
            X = v1;
            Y = v2;
        }

        public Point ToPoint()
        {
            return Vector.ToPoint(this);
        }

        public void Normalise()
        {
            Vector normalized = Vector.Normalise(this);
            this.X = normalized.X;
            this.Y = normalized.Y;
        }

        public static int Dot(Vector v1, Vector v2)
        {
            return (v1.X * v2.X) + (v1.Y * v2.Y);
        }

        public static Vector Normalise(Vector v)
        {
            int modulus = (int)Math.Sqrt((v.X * v.X) + (v.Y * v.Y));
            return v / modulus;
        }

        public static Vector operator +(Vector v1, Vector v2)
        {
            return new Vector(v1.X + v2.X, v1.Y + v2.Y);
        }

        public static Vector operator -(Vector v1, Vector v2)
        {
            return new Vector(v1.X - v2.X, v1.Y - v2.Y);
        }

        public static Vector operator /(Vector v1, int i)
        {
            return new Vector(v1.X / i, v1.Y / i);
        }

        public static Vector operator *(Vector v1, int i)
        {
            return new Vector(v1.X * i, v1.Y * i);
        }

        public static Vector FromPoint(Point p)
        {
            return new Vector(p.X, p.Y);
        }

        public static Point ToPoint(Vector v)
        {
            return new Point(v.X, v.Y);
        }
    }

    public class ConvexHull
    {
        public Point[] Vertices;
        private bool[] backFacing;
        public Point Position;

        public ConvexHull()
        {
            Vertices = new Point[4] { new Point(0, 0), new Point(0, 100), new Point(100, 100), new Point(100, 0) };
            backFacing = new bool[4];
        }

        public Point[] ComputeLighting(Point lightPos)
        {
            //compute facing of each edge, using N*L
            for (int i = 0; i < Vertices.Length; i++)
            {
                Vector firstVertex = Vector.FromPoint(Vertices[i]) + Vector.FromPoint(Position);
                int secondI = (i + 1) % Vertices.Length;
                Vector secondVertex = Vector.FromPoint(Vertices[secondI]) + Vector.FromPoint(Position);
                Vector middle = (firstVertex + secondVertex) / 2;

                Vector L = Vector.FromPoint(lightPos);

                Vector N = new Vector();
                N.X = -(secondVertex.Y - firstVertex.Y);
                N.Y = secondVertex.X - firstVertex.X;

                if (Vector.Dot(N, L) > 0)
                    backFacing[i] = false;
                else
                    backFacing[i] = true;
            }

            // Find starting and ending vertex of shadow
            int startingIndex = 0;
            int endingIndex = 0;
            for (int i = 0; i < Vertices.Length; i++)
            {
                int currentEdge = i;
                int nextEdge = (i + 1) % Vertices.Length;

                if (backFacing[currentEdge] && !backFacing[nextEdge])
                    endingIndex = nextEdge;

                if (!backFacing[currentEdge] && backFacing[nextEdge])
                    startingIndex = nextEdge;
            }

            int shadowVertexCount;

            //nr of vertices that are in the shadow

            if (endingIndex > startingIndex)
                shadowVertexCount = endingIndex - startingIndex + 1;
            else
                shadowVertexCount = Vertices.Length + 1 - startingIndex + endingIndex;

            Point[] shadowVertices = new Point[shadowVertexCount * 2];

            //create a triangle strip that has the shape of the shadow
            int currentIndex = startingIndex;
            int svCount = 0;
            while (svCount != shadowVertexCount * 2)
            {
                Vector vertexPos = Vector.FromPoint(Vertices[currentIndex]) + Vector.FromPoint(Position);

                //one vertex on the hull
                shadowVertices[svCount] = vertexPos.ToPoint();

                //one extruded by the light direction
                Vector L2P = vertexPos - Vector.FromPoint(lightPos);
                L2P.Normalise();
                shadowVertices[svCount + 1] = (Vector.FromPoint(lightPos) + (L2P * 9000)).ToPoint();

                svCount += 2;
                currentIndex = (currentIndex + 1) % Vertices.Length;
            }

            return shadowVertices;
        }
    }
}
